package graph_viewer.structure.builder;

import java.awt.*;
import java.io.*;
import java.util.*;

import graph_viewer.app_exception.*;
import graph_viewer.util.*;
import graph_viewer.structure.*;

public class Clustered_Graph_Builder extends Basic_Builder{
	public static void main(String arg[])  throws IOException{	
		Clustered_Graph_Builder tgb	= new Clustered_Graph_Builder( "c488.cls" );
		
		Tree	tree				= new Generic_Tree();
		Tree created_tree	= tgb.build_tree( tree );
		Clustered_Graph cg	= (Clustered_Graph)tgb.build_clustered_graph( created_tree );
		cg.traverse(
			new Command(){
				public void do_action( Node node ){
					Debug.println( "\t[ " + node + " ]", 3 );
				}
			} 	
		);
		//Debug.println( "created_tree has " + created_tree.get_tree().size() + " nodes" , 3 );
		//Structure_Util.traverse_tree( created_tree );
	}	

	private File	node_data;

	//============================================================================
	// constructor
	//============================================================================	
	public Clustered_Graph_Builder( String a_file_name ) throws IOException{
		this( new File(a_file_name) );		
	}

	public Clustered_Graph_Builder( File a_file ) throws IOException{
		if ( is_valid_data_file(a_file) ){
			set_file( a_file );
			////Debug.println( " File is NOT null " , 2);
		} else {
			//Debug.println( " File is null " , 2);
			node_data	= null;
		}
	}

	//============================================================================
	// setter&getter
	//============================================================================
	public void set_file( File a_file ){
		node_data	= a_file;
	}

	public File get_file(){
		return this.node_data;
	}
	//============================================================================
	// public methods
	//============================================================================
	public Graph build_clustered_graph( Tree cluster_tree ) throws IOException {
		Tree created_cluster_tree	 = build_tree( cluster_tree );

		Clustered_Graph	a_c_g	= new Clustered_Graph( created_cluster_tree );
		ArrayList			edges	= build_edges( a_c_g );
		a_c_g.set_edges( edges );

		return a_c_g;
	}
	
	
	//the first element of collection must be the node
	public Tree build_tree( Tree tree ) throws IOException{
		ArrayList	node_list	 = create_node_basic_info();
		create_parent_children_relation( node_list );
		create_node_type( node_list );

		
		//Debug.println("====================build_tree==================================", 2 );

		Tree created_tree = new Generic_Tree( node_list );
		
		
		ArrayList clone_node_list	= new ArrayList( node_list );
		for ( Iterator _i = clone_node_list.iterator(); _i.hasNext(); ){
			Node node	= (Node)_i.next();

			if ( node.get_parent() == null ){
				Node root	= created_tree.search( node.get_label() );
				
				created_tree.set_root( root );
			}
		}
		//to-do: node position
		//Debug.println("===================build_tree end==================================", 2);
		
		return created_tree;
	}

	public ArrayList build_edges( Graph a_graph ) throws IOException{
		ArrayList	 edges	= new ArrayList();
		ArrayList edges_info = create_information_list( "normal edges" );
		
		
		for ( Iterator iter=edges_info.iterator(); iter.hasNext(); ){
			String an_edge			= (String)iter.next();	

			int	 sep_index	= an_edge.indexOf( " " );
			//Debug.println( "sep_index build_edges: " + sep_index, 3 );

			String	 
				start_label	= an_edge.substring( 0, sep_index ),
				end_label	= an_edge.substring( sep_index+1 );

			//Debug.println( "build_edges: " + start_label + "---" + start_label, 2 );
			edges.add( new String[]{ start_label, end_label } );
		}		

		return edges;
	}



	//============================================================================
	// private methods
	//============================================================================
	//@return a node with label and text
	private ArrayList create_node_basic_info() throws IOException{
		ArrayList nodes_with_basic_info = new ArrayList();

		ArrayList node_text_list	 = create_information_list( "node text" );

		for ( Iterator _i = node_text_list.iterator(); _i.hasNext(); ){
			String node_text	= (String)_i.next();		
			
			//Debug.println( "create_node_basic_info by: " + node_text , 3 );
			String	 node_label, node_detail;

			Node	a_node	= new Node();
			int	 sep_index	= node_text.indexOf( " " );
			//Debug.println( "sep_index create_node_basic_info: " + sep_index, 3 );
			
			if ( sep_index > 0 ){
				node_label	= node_text.substring( 0, sep_index );
				
				if ( node_text.length() > sep_index ){
					node_detail	= node_text.substring( sep_index+1 );
					a_node.set_text( node_detail );			
				}
			}else{
				node_label	= node_text;
			}
				
			a_node.set_label( node_label );
			
			nodes_with_basic_info.add( a_node );
		}
		
		//for //Debug//
		//Debug.println( " create_node_basic_info find: ", 3 );
		for ( Iterator _i = nodes_with_basic_info.iterator(); _i.hasNext(); ){
			Node node	= (Node)_i.next();

			//Debug.println( node.toString(), 3 );
		}

		return nodes_with_basic_info;
	}	

	// find a node from a node list by the label
	private Node find_by_label( String label, ArrayList nodes ){
		for ( Iterator _i = nodes.iterator(); _i.hasNext(); ){
			Node node	= (Node)_i.next();

			if ( node.get_label().equals( label ) ){
				return node;
			}
		}
		
		return null;
	}

	//node parent and children relation is stored in the section of cluster edges, which are hierarchical edges.
	private void create_parent_children_relation( ArrayList node_text_list ) throws IOException{		
		ArrayList	 node_parent_children_relations	= create_information_list( "cluster edges" );

		for ( Iterator _i = node_parent_children_relations.iterator(); _i.hasNext(); ){
			String node_parent_children_relation = (String)_i.next();
			//Debug.println( "node_parent_children_relation: " + node_parent_children_relation, 3 );
			int	 sep_index	= node_parent_children_relation.indexOf( " " );
			//Debug.println( "sep_index create_parent_children_relation: " + sep_index, 3 );

			String	 
				parent_label	= node_parent_children_relation.substring( 0, sep_index ),
				child_label		= node_parent_children_relation.substring( sep_index+1 );			
			
			//Debug.println( "parent_label: " + parent_label, 3 );
			//Debug.println( "child_label: " + child_label, 3 );

			Node
				parent	= find_by_label( parent_label, node_text_list ),
				child		= find_by_label( child_label, node_text_list );

			//Debug.println( "parent node: " + parent, 3 );
			//Debug.println( "child node: " + child, 3 );

			if ( parent != null && child != null ){
				parent.add_child(child);
				child.set_parent( parent );
				//Debug.println( "parent_children_relation Node type created!", 3 );
			}else{
				throw new App_Exception( "Invalid node label ocurred when Clustered_Graph_Builder create_parent_children_relation!" );
			}
		}
	}

	//node parent and children relation is stored in the section of cluster edges, which are hierarchical edges.
	private void create_node_type( ArrayList node_text_list )  throws IOException{		
		ArrayList	 node_type_list	= create_information_list( "node types" );
		//Debug.println( "node_type_list created!", 3 );

		for ( Iterator _i = node_type_list.iterator(); _i.hasNext(); ){
			String node_type = (String)_i.next();
			
			int	 sep_index	= node_type.indexOf( " " );
			//Debug.println( "sep_index create_node_type: " + sep_index, 3 );

			String	 
				label	= node_type.substring( 0, sep_index ),
				type	= node_type.substring( sep_index+1 );
			
			Node
				node	= find_by_label( label, node_text_list );

			if ( node != null ){
				node.set_type( type );
				//Debug.println( "Node type created!", 3 );
			}else{
				throw new App_Exception( "Invalid node label ocurred when Clustered_Graph_Builder create_node_type!" );
			}
		}		
	}
	
	//@ all the edges of ArrayList, element is String[] { node_labal_start, node_labal_end }
	private ArrayList create_edges_info() throws IOException{
		BufferedReader	node_data_reader	= new BufferedReader( new InputStreamReader( new FileInputStream( this.node_data ) ) );

		ArrayList		edges_info				= new ArrayList();

		while ( true ){
			String edge_list_string = node_data_reader.readLine().trim() ;
			
			if ( is_end_flag(edge_list_string ) ){
				break;
			}

			if ( edge_list_string.startsWith( edge ) ){
				//ArrayList edge	= retrive_edge_information( edge_list_string );
				String[] edge	= retrive_edge_information( edge_list_string );
				
				edges_info.add(edge);
			}
	
		}

		node_data_reader.close();

		return edges_info;
	}

	//@param	edge_information_list	a string read out from a file. e.x: !	a1	 b1
	private String[] retrive_edge_information( String edge_infomation_list ){
		ArrayList	edge_list		= new ArrayList();
		
		StringTokenizer st	= new StringTokenizer( edge_infomation_list );
		while ( st.hasMoreTokens() ){
			edge_list.add( st.nextToken() );
		}

		edge_list.remove( 0 );//remove starting flag

		String 
			start_label	= (String)edge_list.get(0),
			end_label	= (String)edge_list.get(1);

		return new String[] {start_label, end_label};
	}


	/**
		find information related to @param information_category
	*/
	private ArrayList create_information_list( String information_category ) throws IOException{
		return create_information_list_by_category( this.node_data, information_category );
	}
};
